/*****************************************************************************
 *  This file is part of the OpenDomo project.
 *  Copyright(C) 2011 OpenDomo Services SL
 *  
 *  Daniel Lerch Hostalot <dlerch@opendomo.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *****************************************************************************/

// ----------------------------------------------------------------------------
// author: Daniel Lerch <dlerch@gmail.com>, http://opendomo.com
// date: Mayo 2007
//
// CHANGES:
//
// ----------------------------------------------------------------------------



#include <stdlib.h>
#include <upnp_query.h>

UpnpClient_Handle hdl = -1;


// {{{ upnp_query_callback()
int upnp_query_callback(Upnp_EventType event_type, void *event, void *cookie)
{
   switch(event_type) 
   {
      case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
      {
         //printf("UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n");
         break;
      }
      case UPNP_DISCOVERY_SEARCH_RESULT:
      {
         //printf("UPNP_DISCOVERY_SEARCH_RESULT\n");
         struct Upnp_Discovery *dsc = (struct Upnp_Discovery *)event;
         printf("%s : ", dsc->DeviceId);
         printf("%s\n", dsc->Location);
      }

      case UPNP_DISCOVERY_SEARCH_TIMEOUT:
      {
         //printf("UPNP_DISCOVERY_SEARCH_TIMEOUT\n");
         break;
      }

      case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
      {
         //printf("UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n");
         struct Upnp_Discovery *dsc = (struct Upnp_Discovery *)event;
         printf("DeviceId: %s bye\n", dsc->DeviceId);
         break;
      }

      case UPNP_CONTROL_ACTION_REQUEST:
      {
         //printf("UPNP_CONTROL_ACTION_REQUEST\n");
         break;
      }

      case UPNP_CONTROL_ACTION_COMPLETE:
      {
         //printf("UPNP_CONTROL_ACTION_COMPLETE\n");
       
         /*  
         struct Upnp_Action_Complete *action = 
            (struct Upnp_Action_Complete*) event;

         char *value = util_get_first_document_item(action->ActionResult, 
               (char*)cookie);

         if(value)
         {  
            printf("%s\n", value);
            free(value);
         }
         */
         break;
      }

      case UPNP_CONTROL_GET_VAR_COMPLETE:
      {
         //printf("UPNP_CONTROL_GET_VAR_COMPLETE\n");
         struct Upnp_State_Var_Complete *sv_event = 
            (struct Upnp_State_Var_Complete * )event;

         if(sv_event->ErrCode != UPNP_E_SUCCESS) 
         {
            printf("[upnp_query], upnp_query_callback() error: %d\n",sv_event->ErrCode);
         } 
         else 
         {
            //printf("%s\n", sv_event->StateVarName);
            printf("%s\n", sv_event->CurrentVal);
         }


         break;
      }

      case UPNP_EVENT_RECEIVED:
      {
         printf("UPNP_EVENT_RECEIVED\n");
         break;
      }

      case UPNP_EVENT_SUBSCRIBE_COMPLETE:
      {
         //printf("UPNP_EVENT_SUBSCRIBE_COMPLETE\n");
         break;
      }

      case UPNP_EVENT_UNSUBSCRIBE_COMPLETE:
      {
         //printf("UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n");
         break;
      }

      case UPNP_EVENT_RENEWAL_COMPLETE:
      {
         //printf("UPNP_EVENT_RENEWAL_COMPLETE\n");
         break;
      }

      case UPNP_EVENT_AUTORENEWAL_FAILED:
      {
         //printf("UPNP_EVENT_AUTORENEWAL_FAILED\n");
         break;
      }

      case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
      {
         //printf("UPNP_EVENT_SUBSCRIPTION_EXPIRED\n");
         break;
      }

      case UPNP_EVENT_SUBSCRIPTION_REQUEST:
      {
         //printf("UPNP_EVENT_SUBSCRIPTION_REQUEST\n");
         break;
      }

      case UPNP_CONTROL_GET_VAR_REQUEST:
      {
         //printf("UPNP_CONTROL_GET_VAR_REQUEST\n");
         break;
      }

      
      break;
   }

   return 0;
}
// }}}

// {{{ upnp_query_init()
int upnp_query_init()
{
   int ret=0;

   /* Initialize the UPnP Library */
   if( (ret=UpnpInit(NULL, 0)) != UPNP_E_SUCCESS)
   {
      fprintf(stderr, "[upnp_query], UpnpInit() error %d: "
             "line %d , file %s \n", ret, __LINE__, __FILE__);
      return ret;
   }

   /* Register as a client */
   if( (ret=UpnpRegisterClient(upnp_query_callback, &hdl, &hdl)) != UPNP_E_SUCCESS)
   {
      fprintf(stderr, "[upnp_query], UpnpRegisterClient() error %d: "
             "line %d , file %s \n", ret, __LINE__, __FILE__);
      return ret;
   }

   return 0;
}
// }}}

// {{{ upnp_query_cleanup()
int upnp_query_cleanup()
{
   int ret=0;

   /* Initialize the UPnP Library */
   if( (ret=UpnpUnRegisterClient(hdl)) != UPNP_E_SUCCESS)
   {
      fprintf(stderr, "[upnp_query], UpnpUnRegisterClient() error %d: "
             "line %d , file %s \n", ret, __LINE__, __FILE__);
      return ret;
   }

   UpnpFinish();

   return 0;
}
// }}}

// {{{ upnp_query_print_device_list()
int upnp_query_print_device_list(int listen_time)
{
   int ret=0;

   if(upnp_query_init() != 0)
   {
      fprintf(stderr, "[upnp_query], upnp_query_init() error: "
             "line %d , file %s \n", __LINE__, __FILE__);
      return -1;
   }

   /* Search request for all devices */
   if( (ret=UpnpSearchAsync(hdl,5, "upnp:rootdevice", NULL)) != UPNP_E_SUCCESS)
   {
      fprintf(stderr, "[upnp_query], UpnpSearchAsync() error %d: "
             "line %d , file %s \n", ret, __LINE__, __FILE__);
      return -1;
   }

   /* wait listen_time seconds */
   printf("Listening ...\n");
   sleep(listen_time);


   if(upnp_query_cleanup() != 0)
   {
      fprintf(stderr, "[upnp_query], upnp_query_cleanup() error: "
             "line %d , file %s \n", __LINE__, __FILE__);
      return -1;
   }

   return 0;
}
// }}}

// {{{ upnp_query_print_device_info()
int upnp_query_print_device_info(const char *data, const char *action_url)
{
   int ret;

   /*
   if(upnp_query_init() != 0)
   {
      fprintf(stderr, "[upnp_query], upnp_query_init() error: "
             "line %d , file %s \n", __LINE__, __FILE__);
      return -1;
   }
   */

   // TODO: search request de un uuid y descargar su XML para obterner
   //       informacion de servicios, metodos, etc.
   IXML_Document *doc = NULL;

   if( (ret=UpnpDownloadXmlDoc(action_url, &doc)) != UPNP_E_SUCCESS )
   {
      fprintf(stderr, "[upnp_query], UpnpDownloadXmlDoc() error %d: "
             "line %d , file %s \n", ret, __LINE__, __FILE__);
      return -1;
   }

   char *tmp = util_get_first_document_item(doc, "presentationURL");

   printf("%s\n", tmp);

   if(tmp)
      free(tmp);

   if(doc)
      ixmlDocument_free(doc);

   /*
   if(upnp_query_cleanup() != 0)
   {
      printf("[upnp_query], upnp_query_cleanup() error\n");
      fprintf(stderr, "[upnp_query], upnp_query_cleanup() error: "
             "line %d , file %s \n", __LINE__, __FILE__);
      return -1;
   }*/

   return 0;
}
// }}}

// {{{ upnp_query_print_xmldoc()
int upnp_query_print_xmldoc(const char *xml_url)
{
   int ret;
   IXML_Document *doc = NULL;

   if( (ret=UpnpDownloadXmlDoc(xml_url, &doc)) != UPNP_E_SUCCESS )
   {
      fprintf(stderr, "[upnp_query], UpnpDownloadXmlDoc() error %d: "
             "line %d , file %s \n", ret, __LINE__, __FILE__);
      return -1;
   }

   char *tmp =  ixmlPrintNode((IXML_Node*)doc);

   if(tmp)
   {
      printf("%s\n", tmp);
      free(tmp);
   }

   return 0;
}
// }}}

// {{{ upnp_query_print_xmlinfo()
int upnp_query_print_xmlinfo(const char *xml_url)
{
	IXML_Document *doc = NULL;
	IXML_NodeList *node_list = NULL;
	IXML_Node *node = NULL;
	int ret=0;
	char url[256];

	// Get XML device descriptor
	if( (ret=UpnpDownloadXmlDoc(xml_url, &doc)) != UPNP_E_SUCCESS )
	{
		fprintf(stderr, "[upnp_query], UpnpDownloadXmlDoc() %s error %d: "
				"line %d , file %s \n", xml_url, ret, __LINE__, __FILE__);
		ret = -1;
		goto error;
	}

	// Get controlURL from XML device descriptor
	char *controlurl = util_get_first_document_item(doc, "controlURL");
	if(!controlurl)
	{
		fprintf(stderr, "[upnp_query], controlURL not found: "
				"line %d , file %s \n", __LINE__, __FILE__);
		ret = -1;
		goto error;
	}

	// Get SCPDURL from XML device descriptor
	char *scpdurl = util_get_first_document_item(doc, "SCPDURL");
	if(!scpdurl)
	{
		fprintf(stderr, "[upnp_query], SCPDURL not found: "
				"line %d , file %s \n", __LINE__, __FILE__);
		ret = -1;
		goto error;
	}

	// Create scpd url
	snprintf(url, sizeof(url), "%s", xml_url);
	char *ptr = url+strlen(url);
	while(*ptr != '/' && *ptr != xml_url[0]) ptr--;

	int d=1;
	if(scpdurl[0]=='/') d=0;

	if(*ptr == '/')
	{
		*(ptr+d) = 0;	
		strncat(url, scpdurl, sizeof(url));
		url[sizeof(url)-1]=0;
	}
	
	// Free XML document
	if(doc)
		ixmlDocument_free(doc);

	// Get XML SCPD document
	if( (ret=UpnpDownloadXmlDoc(url, &doc)) != UPNP_E_SUCCESS )
	{
		fprintf(stderr, "[upnp_query], UpnpDownloadXmlDoc() %s error %d: "
				"line %d , file %s \n", url, ret, __LINE__, __FILE__);
		ret = -1;
		goto error;
	}

	// Get SCPD Node
	node_list = ixmlDocument_getElementsByTagName(doc, "action");
	if(node_list)
	{
		int i=0;
		for(i=0; i<ixmlNodeList_length(node_list); i++)
		{
			IXML_Node *n = ixmlNodeList_item(node_list, i);
		
			printf("%s - %s\n", ixmlNode_getNodeName(n), ixmlNode_getNodeValue(n));
		}
	}


	/*
   if(node_list) 
   	node = ixmlNodeList_item(node_list, 0);

	if(!node)
	{
		fprintf(stderr, "[upnp_query], ixmlDocument_getElementsByTagName() "
				"error: scpd not found. "
				"line %d , file %s \n", __LINE__, __FILE__);
		ret = -1;
		goto error;
	}

	// Free Node list
   if(node_list)
    	ixmlNodeList_free(node_list);

	// Get child nodes
	node_list = ixmlNode_getChildNodes(node);  
	if(node_list)
	{
		int i=0;
		for(i=0; i<ixmlNodeList_length(node_list); i++)
		{
			IXML_Node *n = ixmlNodeList_item(node_list, i);
		
			printf("%s - %s\n", ixmlNode_getNodeName(n), ixmlNode_getNodeValue(n));
		}
	}
	*/

error:
	// Free allocated memory
   if(node_list)
    	ixmlNodeList_free(node_list);

	if(controlurl)
		free(controlurl);

	if(scpdurl)
		free(scpdurl);

	if(doc)
		ixmlDocument_free(doc);

	return 0;
}
// }}}

// {{{ upnp_query_query_xml_field()
int upnp_query_query_xml_field(const char *field_name, 
      const char *xml_url, char *value, size_t value_len)
{
   int ret;
   IXML_Document *doc = NULL;

   if( (ret=UpnpDownloadXmlDoc(xml_url, &doc)) != UPNP_E_SUCCESS )
   {
      fprintf(stderr, "[upnp_query], UpnpDownloadXmlDoc() error %d: "
             "line %d , file %s \n", ret, __LINE__, __FILE__);
      return -1;
   }

   char *tmp = util_get_first_document_item(doc, field_name);


   if(tmp)
   {
      strNcpy(value, tmp, value_len);
      free(tmp);
   }

   if(doc)
      ixmlDocument_free(doc);

   return 0;
}
// }}}

// {{{ upnp_query_get_var()
int upnp_query_get_var(const char *varname, const char *action_url)
{
   int ret=0;

   if(upnp_query_init() != 0)
   {
      fprintf(stderr, "[upnp_query], upnp_query_init() error: "
             "line %d , file %s \n", __LINE__, __FILE__);
      return -1;
   }

   /* Request variable value */
   if( (ret=UpnpGetServiceVarStatusAsync(hdl, action_url, varname, 
               upnp_query_callback, NULL)) != UPNP_E_SUCCESS)
   {
      fprintf(stderr, "[upnp_query], UpnpGetServiceVarStatusAsync() error %d: "
             "line %d , file %s \n", ret, __LINE__, __FILE__);
      return -1;
   }  

   if(upnp_query_cleanup() != 0)
   {
      fprintf(stderr, "[upnp_query], upnp_query_cleanup() error: "
             "line %d , file %s \n", __LINE__, __FILE__);
      return -1;
   }

   return 0;
}
// }}}

// {{{ upnp_query_exec_action()
int upnp_query_exec_action(const char *action_name, const char *csv_params, 
                       const char *action_url, const char *service_type)
{
   IXML_Document *action = NULL;
   int ret=0;
   char *p;
   char *q;
   char *tokptr1;
   char *tokptr2;

   if(upnp_query_init() != 0)
   {
      fprintf(stderr, "[upnp_query], upnp_query_init() error: "
             "line %d , file %s \n", __LINE__, __FILE__);
      return -1;
   }


   /* action xml */
   for(q=(char*)csv_params;  (p=strtok_r(q, ",", &tokptr1)) != NULL; q = NULL)
   {
      char *param_name = strtok_r(p, "=", &tokptr2);
      char *param_value = strtok_r(NULL, "=", &tokptr2);

      if(!param_name || !param_value)
      {
         fprintf(stderr, "[upnp_query], upnp_query_exec_action() param error: "
                "line %d , file %s \n", __LINE__, __FILE__);
         return -1;
      }

      if( (ret=UpnpAddToAction(&action, action_name, service_type, 
               param_name, param_value)) != UPNP_E_SUCCESS)
      {
         fprintf(stderr, "[upnp_query], UpnpAddToAction() error %d: "
                "line %d , file %s \n", ret, __LINE__, __FILE__);
         return -1;
      }

   }

   /* Send action */
   if( (ret=UpnpSendActionAsync(hdl, action_url, service_type,
         NULL, action, upnp_query_callback, NULL)) != UPNP_E_SUCCESS)
   {
      fprintf(stderr, "[upnp_query], UpnpSendActionAsync() error %d: "
             "line %d , file %s \n", ret, __LINE__, __FILE__);
      return -1;
   }

   if(action)
      ixmlDocument_free(action);



   if(upnp_query_cleanup() != 0)
   {
      fprintf(stderr, "[upnp_query], upnp_query_cleanup() error: "
             "line %d , file %s \n", __LINE__, __FILE__);
      return -1;
   }

   return 0;
}
// }}}



